﻿using System;
using System.Collections.Generic;
using System.Text;

namespace testca
{
    class PhysMemStack
    {
        ulong entries = 0;
        ulong next = 0;

        public ulong Count { get { return entries; } }
        public void Push(ulong vaddr, ulong paddr)
        {
            // TODO: lock here
            Kernel.Kernel.poke(vaddr, next);
            next = paddr;
            entries++;
            // TODO: unlock here
        }
        public ulong BeginPop()
        {
            if (entries == 0)
                throw new Exception();
            // TODO: lock here
            return next;
        }
        public void EndPop(ulong vaddr)
        {
            next = Kernel.Kernel.peek_U8(vaddr);
            entries--;
            // TODO: unlock here
        }
    }

    class PhysMemBitmap
    {
        uint[] bmp;
        ulong bmp_start, bmp_end, ps;
        ulong elem_count;

        ulong free = 0;
        public ulong Count { get { return free; } }
        public ulong Alloc()
        {
            // Return the first free page
            for (ulong i = 0; i < elem_count; i++)
            {
                if (bmp[i] == 0x00000000)
                    continue;
                for (int j = 0; j < 32; j++)
                {
                    if (((bmp[i] >> j) & 0x1) == 0x1)
                    {
                        // TODO make this atomic
                        bmp[i] |= (0x1U << j);
                        free--;
                        return bmp_start + (i * 32 + (ulong)j) * ps;
                    }
                }
            }
            return 0;
        }
        public ulong Alloc(ulong paddr)
        {
            ulong bit_addr = (paddr - bmp_start) / ps;
            ulong dword_offset = bit_addr / 32;
            int bit_offset = (int)(bit_addr % 32);

            // TODO atomic
            if (((bmp[dword_offset] >> bit_offset) & 0x1) == 0x1)
            {
                bmp[dword_offset] |= (0x1U << bit_offset);
                free--;
                return paddr;
            }
            return 0;
        }
        public ulong AllocLength(ulong length) { return 0; }
        public void Free(ulong paddr)
        {
            ulong bit_addr = (paddr - bmp_start) / ps;
            ulong dword_offset = bit_addr / 32;
            int bit_offset = (int)(bit_addr % 32);

            // TODO atomic
            bmp[dword_offset] &= ~(0x1U << bit_offset);
            free++;
        }
        public PhysMemBitmap(ulong bitmap_base, ulong bitmap_end, ulong page_size)
        {
            ulong num_pages = (bitmap_end - bitmap_base) / page_size;
            elem_count = num_pages / 32;
            bmp = new uint[elem_count];
            bmp_start = bitmap_base;
            bmp_end = bitmap_end;
            ps = page_size;
        }
    }
}
